// Free Disassembler and Assembler -- Disassembler
//
// Copyright (C) 2001 Oleh Yuschuk
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


#define STRICT

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//#include <dir.h>
#include <math.h>
#include <float.h>
#pragma hdrstop

#include "disasm.h"

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////// GLOBAL VARIABLES ///////////////////////////////
// AdjWang: I don't konw why these global vars are placed in disasm.h and using
// a MAINPROG macro to control linking. I would put them here.
int       ideal;                // Force IDEAL decoding mode
int       lowercase;            // Force lowercase display
int       tabarguments;         // Tab between mnemonic and arguments
int       extraspace;           // Extra space between arguments
int       putdefseg;            // Display default segments in listing
int       showmemsize;          // Always show memory size
int       shownear;             // Show NEAR modifiers
int       shortstringcmds;      // Use short form of string commands
int       sizesens;             // How to decode size-sensitive mnemonics
int       symbolic;             // Show symbolic addresses in disasm
int       farcalls;             // Accept far calls, returns & addresses
int       decodevxd;            // Decode VxD calls (Win95/98)
int       privileged;           // Accept privileged commands
int       iocommand;            // Accept I/O commands
int       badshift;             // Accept shift out of range 1..31
int       extraprefix;          // Accept superfluous prefixes
int       lockedbus;            // Accept LOCK prefixes
int       stackalign;           // Accept unaligned stack operations
int       iswindowsnt;          // When checking for dangers, assume NT

//////////////////////////// DISASSEMBLER FUNCTIONS ////////////////////////////

// Work variables of disassembler
static ulong     datasize;             // Size of data (1,2,4 bytes)
static ulong     addrsize;             // Size of address (2 or 4 bytes)
static int       segprefix;            // Segment override prefix or SEG_UNDEF
static int       hasrm;                // Command has ModR/M byte
static int       hassib;               // Command has SIB byte
static int       dispsize;             // Size of displacement (if any)
static int       immsize;              // Size of immediate data (if any)
static int       softerror;            // Noncritical disassembler error
static int       ndump;                // Current length of command dump
static int       nresult;              // Current length of disassembly
static int       addcomment;           // Comment value of operand

// Copy of input parameters of function Disasm()
static char* cmd;                 // Pointer to binary data
static char* pfixup;              // Pointer to possible fixups or NULL
static ulong     size;                 // Remaining size of the command buffer
static t_disasm* da;                  // Pointer to disassembly results
static int       mode;                 // Disassembly mode (DISASM_xxx)

// Disassemble name of 1, 2 or 4-byte general-purpose integer register and, if
// requested and available, dump its contents. Parameter type changes decoding
// of contents for some operand types.
static void DecodeRG(int index, int datasize, int type) {
    int sizeindex;
    char name[9];
    if (mode < DISASM_DATA) return;        // No need to decode
    index &= 0x07;
    if (datasize == 1)
        sizeindex = 0;
    else if (datasize == 2)
        sizeindex = 1;
    else if (datasize == 4)
        sizeindex = 2;
    else {
        da->error = DAE_INTERN; return;
    };
    if (mode >= DISASM_FILE) {
        strcpy(name, regname[sizeindex][index]);
        if (lowercase) strlwr(name);
        if (type < PSEUDOOP)                 // Not a pseudooperand
            nresult += sprintf(da->result + nresult, "%s", name);
        ;
    };
};

// Disassemble name of 80-bit floating-point register and, if available, dump
// its contents.
static void DecodeST(int index, int pseudoop) {
    int i;
    char s[32];
    if (mode < DISASM_FILE) return;        // No need to decode
    index &= 0x07;
    i = sprintf(s, "%s(%i)", (lowercase ? "st" : "ST"), index);
    if (pseudoop == 0) {
        strcpy(da->result + nresult, s);
        nresult += i;
    };
};

// Disassemble name of 64-bit MMX register.
static void DecodeMX(int index) {
    char* pr;
    if (mode < DISASM_FILE) return;        // No need to decode
    index &= 0x07;
    pr = da->result + nresult;
    nresult += sprintf(pr, "%s%i", (lowercase ? "mm" : "MM"), index);
};

// Disassemble name of 64-bit 3DNow! register and, if available, dump its
// contents.
static void DecodeNR(int index) {
    char* pr;
    if (mode < DISASM_FILE) return;        // No need to decode
    index &= 0x07;
    pr = da->result + nresult;
    nresult += sprintf(pr, "%s%i", (lowercase ? "mm" : "MM"), index);
};

// Service function, adds valid memory adress in MASM or Ideal format to
// disassembled string. Parameters: defseg - default segment for given
// register combination, descr - fully decoded register part of address,
// offset - constant part of address, dsize - data size in bytes. If global
// flag 'symbolic' is set, function also tries to decode offset as name of
// some label.
static void Memadr(int defseg, const char* descr, long offset, int dsize) {
    int i, n, seg;
    char* pr;
    char s[TEXTLEN];
    if (mode < DISASM_FILE || descr == NULL)
        return;                            // No need or possibility to decode
    pr = da->result + nresult; n = 0;
    if (segprefix != SEG_UNDEF) seg = segprefix; else seg = defseg;
    if (ideal != 0) pr[n++] = '[';
    // In some cases Disassembler may omit size of memory operand. Namely, flag
    // showmemsize must be 0, type bit C_EXPL must be 0 (this bit namely means
    // that explicit operand size is necessary) and type of command must not be
    // C_MMX or C_NOW (because bit C_EXPL has in these cases different meaning).
    // Otherwise, exact size must be supplied.
    if (showmemsize != 0 || (da->cmdtype & C_TYPEMASK) == C_MMX ||
        (da->cmdtype & C_TYPEMASK) == C_NOW || (da->cmdtype & C_EXPL) != 0
        ) {
        if (dsize < sizeof(sizename) / sizeof(sizename[0]))
            n += sprintf(pr + n, "%s %s", sizename[dsize], (ideal == 0 ? "PTR " : ""));
        else
            n += sprintf(pr + n, "(%i-BYTE) %s", dsize, (ideal == 0 ? "PTR " : ""));
        ;
    };
    if ((putdefseg != 0 || seg != defseg) && seg != SEG_UNDEF)
        n += sprintf(pr + n, "%s:", segname[seg]);
    if (ideal == 0) pr[n++] = '[';
    n += sprintf(pr + n, "%s", descr);
    if (lowercase) strlwr(pr);
    if (offset == 0L) {
        if (*descr == '\0') pr[n++] = '0';
    }
    else {
        if (symbolic && mode >= DISASM_CODE)
            i = Decodeaddress(offset, s, TEXTLEN - n - 24, NULL);
        else i = 0;
        if (i > 0) {                         // Offset decoded in symbolic form
            if (*descr != '\0') pr[n++] = '+';
            strcpy(pr + n, s); n += i;
        }
        else if (offset<0 && offset>-16384 && *descr != '\0')
            n += sprintf(pr + n, "-%lX", -offset);
        else {
            if (*descr != '\0') pr[n++] = '+';
            n += sprintf(pr + n, "%lX", offset);
        };
    };
    pr[n++] = ']'; pr[n] = '\0';
    nresult += n;
};

// Disassemble memory/register from the ModRM/SIB bytes and, if available, dump
// address and contents of memory.
static void DecodeMR(int type) {
    int j, memonly, inmemory, seg;
    int c, sib;
    ulong dsize, regsize, addr;
    char s[TEXTLEN];
    if (size < 2) {
        da->error = DAE_CROSS; return;
    };    // ModR/M byte outside the memory block
    hasrm = 1;
    dsize = regsize = datasize;              // Default size of addressed reg/memory
    memonly = 0;                           // Register in ModM field is allowed
    // Size and kind of addressed memory or register in ModM has no influence on
    // the command size, and exact calculations are omitted if only command size
    // is requested. If register is used, optype will be incorrect and we need
    // to correct it later.
    c = cmd[1] & 0xC7;                     // Leave only Mod and M fields
    if (mode >= DISASM_DATA) {
        if ((c & 0xC0) == 0xC0)              // Register operand
            inmemory = 0;
        else                               // Memory operand
            inmemory = 1;
        switch (type) {
        case MRG:                        // Memory/register in ModRM byte
            if (inmemory) {
                if (datasize == 1) da->memtype = DEC_BYTE;
                else if (datasize == 2) da->memtype = DEC_WORD;
                else da->memtype = DEC_DWORD;
            };
            break;
        case MRJ:                        // Memory/reg in ModRM as JUMP target
            if (datasize != 2 && inmemory)
                da->memtype = DEC_DWORD;
            if (mode >= DISASM_FILE && shownear != 0)
                nresult += sprintf(da->result + nresult, "%s ", (lowercase ? "near" : "NEAR"));
            break;
        case MR1:                        // 1-byte memory/register in ModRM byte
            dsize = regsize = 1;
            if (inmemory) da->memtype = DEC_BYTE; break;
        case MR2:                        // 2-byte memory/register in ModRM byte
            dsize = regsize = 2;
            if (inmemory) da->memtype = DEC_WORD; break;
        case MR4:                        // 4-byte memory/register in ModRM byte
        case RR4:                        // 4-byte memory/register (register only)
            dsize = regsize = 4;
            if (inmemory) da->memtype = DEC_DWORD; break;
        case MR8:                        // 8-byte memory/MMX register in ModRM
        case RR8:                        // 8-byte MMX register only in ModRM
            dsize = 8;
            if (inmemory) da->memtype = DEC_QWORD; break;
        case MRD:                        // 8-byte memory/3DNow! register in ModRM
        case RRD:                        // 8-byte memory/3DNow! (register only)
            dsize = 8;
            if (inmemory) da->memtype = DEC_3DNOW; break;
        case MMA:                        // Memory address in ModRM byte for LEA
            memonly = 1; break;
        case MML:                        // Memory in ModRM byte (for LES)
            dsize = datasize + 2; memonly = 1;
            if (datasize == 4 && inmemory)
                da->memtype = DEC_FWORD;
            da->warnings |= DAW_SEGMENT;
            break;
        case MMS:                        // Memory in ModRM byte (as SEG:OFFS)
            dsize = datasize + 2; memonly = 1;
            if (datasize == 4 && inmemory)
                da->memtype = DEC_FWORD;
            if (mode >= DISASM_FILE)
                nresult += sprintf(da->result + nresult, "%s ", (lowercase ? "far" : "FAR"));
            break;
        case MM6:                        // Memory in ModRM (6-byte descriptor)
            dsize = 6; memonly = 1;
            if (inmemory) da->memtype = DEC_FWORD; break;
        case MMB:                        // Two adjacent memory locations (BOUND)
            dsize = (ideal ? datasize : datasize * 2); memonly = 1; break;
        case MD2:                        // Memory in ModRM byte (16-bit integer)
        case MB2:                        // Memory in ModRM byte (16-bit binary)
            dsize = 2; memonly = 1;
            if (inmemory) da->memtype = DEC_WORD; break;
        case MD4:                        // Memory in ModRM byte (32-bit integer)
            dsize = 4; memonly = 1;
            if (inmemory) da->memtype = DEC_DWORD; break;
        case MD8:                        // Memory in ModRM byte (64-bit integer)
            dsize = 8; memonly = 1;
            if (inmemory) da->memtype = DEC_QWORD; break;
        case MDA:                        // Memory in ModRM byte (80-bit BCD)
            dsize = 10; memonly = 1;
            if (inmemory) da->memtype = DEC_TBYTE; break;
        case MF4:                        // Memory in ModRM byte (32-bit float)
            dsize = 4; memonly = 1;
            if (inmemory) da->memtype = DEC_FLOAT4; break;
        case MF8:                        // Memory in ModRM byte (64-bit float)
            dsize = 8; memonly = 1;
            if (inmemory) da->memtype = DEC_FLOAT8; break;
        case MFA:                        // Memory in ModRM byte (80-bit float)
            dsize = 10; memonly = 1;
            if (inmemory) da->memtype = DEC_FLOAT10; break;
        case MFE:                        // Memory in ModRM byte (FPU environment)
            dsize = 28; memonly = 1; break;
        case MFS:                        // Memory in ModRM byte (FPU state)
            dsize = 108; memonly = 1; break;
        case MFX:                        // Memory in ModRM byte (ext. FPU state)
            dsize = 512; memonly = 1; break;
        default:                         // Operand is not in ModM!
            da->error = DAE_INTERN;
            break;
        };
    };
    addr = 0;
    // There are many possibilities to decode ModM/SIB address. The first
    // possibility is register in ModM - general-purpose, MMX or 3DNow!
    if ((c & 0xC0) == 0xC0) {              // Decode register operand
        if (type == MR8 || type == RR8)
            DecodeMX(c);                     // MMX register
        else if (type == MRD || type == RRD)
            DecodeNR(c);                     // 3DNow! register
        else
            DecodeRG(c, regsize, type);        // General-purpose register
        if (memonly != 0)
            softerror = DAE_MEMORY;            // Register where only memory allowed
        return;
    };
    // Next possibility: 16-bit addressing mode, very seldom in 32-bit flat model
    // but still supported by processor. SIB byte is never used here.
    if (addrsize == 2) {
        if (c == 0x06) {                     // Special case of immediate address
            dispsize = 2;
            if (size < 4)
                da->error = DAE_CROSS;           // Disp16 outside the memory block
            else if (mode >= DISASM_DATA) {
                da->adrconst = addr = *(ushort*)(cmd + 2);
                if (addr == 0) da->zeroconst = 1;
                seg = SEG_DS;
                Memadr(seg, "", addr, dsize);
            };
        }
        else {
            da->indexed = 1;
            if ((c & 0xC0) == 0x40) {          // 8-bit signed displacement
                if (size < 3) da->error = DAE_CROSS;
                else addr = (signed char)cmd[2] & 0xFFFF;
                dispsize = 1;
            }
            else if ((c & 0xC0) == 0x80) {     // 16-bit unsigned displacement
                if (size < 4) da->error = DAE_CROSS;
                else addr = *(ushort*)(cmd + 2);
                dispsize = 2;
            };
            if (mode >= DISASM_DATA && da->error == DAE_NOERR) {
                da->adrconst = addr;
                if (addr == 0) da->zeroconst = 1;
                seg = addr16[c & 0x07].defseg;
                Memadr(seg, addr16[c & 0x07].descr, addr, dsize);
            };
        };
    }
    // Next possibility: immediate 32-bit address.
    else if (c == 0x05) {                  // Special case of immediate address
        dispsize = 4;
        if (size < 6)
            da->error = DAE_CROSS;             // Disp32 outside the memory block
        else if (mode >= DISASM_DATA) {
            da->adrconst = addr = *(ulong*)(cmd + 2);
            if (pfixup == NULL) pfixup = cmd + 2;
            da->fixupsize += 4;
            if (addr == 0) da->zeroconst = 1;
            seg = SEG_DS;
            Memadr(seg, "", addr, dsize);
        };
    }
    // Next possibility: 32-bit address with SIB byte.
    else if ((c & 0x07) == 0x04) {         // SIB addresation
        sib = cmd[2]; hassib = 1;
        *s = '\0';
        if (c == 0x04 && (sib & 0x07) == 0x05) {
            dispsize = 4;                      // Immediate address without base
            if (size < 7)
                da->error = DAE_CROSS;           // Disp32 outside the memory block
            else {
                da->adrconst = addr = *(ulong*)(cmd + 3);
                if (pfixup == NULL) pfixup = cmd + 3;
                da->fixupsize += 4;
                if (addr == 0) da->zeroconst = 1;
                if ((sib & 0x38) != 0x20) {      // Index register present
                    da->indexed = 1;
                    if (type == MRJ) da->jmptable = addr;
                };
                seg = SEG_DS;
            };
        }
        else {                             // Base and, eventually, displacement
            if ((c & 0xC0) == 0x40) {          // 8-bit displacement
                dispsize = 1;
                if (size < 4) da->error = DAE_CROSS;
                else {
                    da->adrconst = addr = (signed char)cmd[3];
                    if (addr == 0) da->zeroconst = 1;
                };
            }
            else if ((c & 0xC0) == 0x80) {     // 32-bit displacement
                dispsize = 4;
                if (size < 7)
                    da->error = DAE_CROSS;         // Disp32 outside the memory block
                else {
                    da->adrconst = addr = *(ulong*)(cmd + 3);
                    if (pfixup == NULL) pfixup = cmd + 3;
                    da->fixupsize += 4;
                    if (addr == 0) da->zeroconst = 1;
                    // Most compilers use address of type [index*4+displacement] to
                    // address jump table (switch). But, for completeness, I allow all
                    // cases which include index with scale 1 or 4, base or both.
                    if (type == MRJ) da->jmptable = addr;
                };
            };
            da->indexed = 1;
            j = sib & 0x07;
            if (mode >= DISASM_FILE) {
                strcpy(s, regname[2][j]);
                seg = addr32[j].defseg;
            };
        };
        if ((sib & 0x38) != 0x20) {          // Scaled index present
            if ((sib & 0xC0) == 0x40) da->indexed = 2;
            else if ((sib & 0xC0) == 0x80) da->indexed = 4;
            else if ((sib & 0xC0) == 0xC0) da->indexed = 8;
            else da->indexed = 1;
        };
        if (mode >= DISASM_FILE && da->error == DAE_NOERR) {
            if ((sib & 0x38) != 0x20) {        // Scaled index present
                if (*s != '\0') strcat(s, "+");
                strcat(s, addr32[(sib >> 3) & 0x07].descr);
                if ((sib & 0xC0) == 0x40) {
                    da->jmptable = 0;              // Hardly a switch!
                    strcat(s, "*2");
                }
                else if ((sib & 0xC0) == 0x80)
                    strcat(s, "*4");
                else if ((sib & 0xC0) == 0xC0) {
                    da->jmptable = 0;              // Hardly a switch!
                    strcat(s, "*8");
                };
            };
            Memadr(seg, s, addr, dsize);
        };
    }
    // Last possibility: 32-bit address without SIB byte.
    else {                               // No SIB
        if ((c & 0xC0) == 0x40) {
            dispsize = 1;
            if (size < 3) da->error = DAE_CROSS; // Disp8 outside the memory block
            else {
                da->adrconst = addr = (signed char)cmd[2];
                if (addr == 0) da->zeroconst = 1;
            };
        }
        else if ((c & 0xC0) == 0x80) {
            dispsize = 4;
            if (size < 6)
                da->error = DAE_CROSS;           // Disp32 outside the memory block
            else {
                da->adrconst = addr = *(ulong*)(cmd + 2);
                if (pfixup == NULL) pfixup = cmd + 2;
                da->fixupsize += 4;
                if (addr == 0) da->zeroconst = 1;
                if (type == MRJ) da->jmptable = addr;
            };
        };
        da->indexed = 1;
        if (mode >= DISASM_FILE && da->error == DAE_NOERR) {
            seg = addr32[c & 0x07].defseg;
            Memadr(seg, addr32[c & 0x07].descr, addr, dsize);
        };
    };
};

// Disassemble implicit source of string operations and, if available, dump
// address and contents.
static void DecodeSO(void) {
    if (mode < DISASM_FILE) return;        // No need to decode
    if (datasize == 1) da->memtype = DEC_BYTE;
    else if (datasize == 2) da->memtype = DEC_WORD;
    else if (datasize == 4) da->memtype = DEC_DWORD;
    da->indexed = 1;
    Memadr(SEG_DS, regname[addrsize == 2 ? 1 : 2][REG_ESI], 0L, datasize);
};

// Disassemble implicit destination of string operations and, if available,
// dump address and contents. Destination always uses segment ES, and this
// setting cannot be overridden.
static void DecodeDE(void) {
    int seg;
    if (mode < DISASM_FILE) return;        // No need to decode
    if (datasize == 1) da->memtype = DEC_BYTE;
    else if (datasize == 2) da->memtype = DEC_WORD;
    else if (datasize == 4) da->memtype = DEC_DWORD;
    da->indexed = 1;
    seg = segprefix; segprefix = SEG_ES;     // Fake Memadr by changing segment prefix
    Memadr(SEG_DS, regname[addrsize == 2 ? 1 : 2][REG_EDI], 0L, datasize);
    segprefix = seg;                       // Restore segment prefix
};

// Decode XLAT operand and, if available, dump address and contents.
static void DecodeXL(void) {
    if (mode < DISASM_FILE) return;        // No need to decode
    da->memtype = DEC_BYTE;
    da->indexed = 1;
    Memadr(SEG_DS, (addrsize == 2 ? "BX+AL" : "EBX+AL"), 0L, 1);
};

// Decode immediate operand of size constsize. If sxt is non-zero, byte operand
// should be sign-extended to sxt bytes. If type of immediate constant assumes
// this, small negative operands may be displayed as signed negative numbers.
// Note that in most cases immediate operands are not shown in comment window.
static void DecodeIM(int constsize, int sxt, int type) {
    int i;
    signed long data;
    ulong l;
    char name[TEXTLEN], comment[TEXTLEN];
    immsize += constsize;                    // Allows several immediate operands
    if (mode < DISASM_DATA) return;
    l = 1 + hasrm + hassib + dispsize + (immsize - constsize);
    data = 0;
    if (size < l + constsize)
        da->error = DAE_CROSS;
    else if (constsize == 1) {
        if (sxt == 0) data = (uchar)cmd[l];
        else data = (signed char)cmd[l];
        if (type == IMS && ((data & 0xE0) != 0 || data == 0)) {
            da->warnings |= DAW_SHIFT;
            da->cmdtype |= C_RARE;
        };
    }
    else if (constsize == 2) {
        if (sxt == 0) data = *(ushort*)(cmd + l);
        else data = *(short*)(cmd + l);
    }
    else {
        data = *(long*)(cmd + l);
        if (pfixup == NULL) pfixup = cmd + l;
        da->fixupsize += 4;
    };
    if (sxt == 2) data &= 0x0000FFFF;
    if (data == 0 && da->error == 0) da->zeroconst = 1;
    // Command ENTER, as an exception from Intel's rules, has two immediate
    // constants. As the second constant is rarely used, I exclude it from
    // search if the first constant is non-zero (which is usually the case).
    if (da->immconst == 0)
        da->immconst = data;
    if (mode >= DISASM_FILE && da->error == DAE_NOERR) {
        if (mode >= DISASM_CODE && type != IMU)
            i = Decodeaddress(data, name, TEXTLEN - nresult - 24, comment);
        else {
            i = 0; comment[0] = '\0';
        };
        if (i != 0 && symbolic != 0) {
            strcpy(da->result + nresult, name); nresult += i;
        }
        else if (type == IMU || type == IMS || type == IM2 || data >= 0 || data < NEGLIMIT)
            nresult += sprintf(da->result + nresult, "%lX", data);
        else
            nresult += sprintf(da->result + nresult, "-%lX", -data);
        if (addcomment && comment[0] != '\0') strcpy(da->comment, comment);
    };
};

// Decode VxD service name (always 4-byte).
static void DecodeVX(void) {
    ulong l, data;
    immsize += 4;                          // Allows several immediate operands
    if (mode < DISASM_DATA) return;
    l = 1 + hasrm + hassib + dispsize + (immsize - 4);
    if (size < l + 4) {
        da->error = DAE_CROSS;
        return;
    };
    data = *(long*)(cmd + l);
    if (data == 0 && da->error == 0) da->zeroconst = 1;
    if (da->immconst == 0)
        da->immconst = data;
    if (mode >= DISASM_FILE && da->error == DAE_NOERR) {
        if ((data & 0x00008000) != 0 && memicmp("VxDCall", da->result, 7) == 0)
            memcpy(da->result, lowercase ? "vxdjump" : "VxDJump", 7);
        nresult += sprintf(da->result + nresult, "%lX", data);
    };
};

// Decode implicit constant 1 (used in shift commands). This operand is so
// insignificant that it is never shown in comment window.
static void DecodeC1(void) {
    if (mode < DISASM_DATA) return;
    da->immconst = 1;
    if (mode >= DISASM_FILE) nresult += sprintf(da->result + nresult, "1");
};

// Decode immediate absolute data address. This operand is used in 8080-
// compatible commands which allow to move data from memory to accumulator and
// back. Note that bytes ModRM and SIB never appear in commands with IA operand.
static void DecodeIA(void) {
    ulong addr;
    if (size < 1 + addrsize) {
        da->error = DAE_CROSS; return;
    };
    dispsize = addrsize;
    if (mode < DISASM_DATA) return;
    if (datasize == 1) da->memtype = DEC_BYTE;
    else if (datasize == 2) da->memtype = DEC_WORD;
    else if (datasize == 4) da->memtype = DEC_DWORD;
    if (addrsize == 2)
        addr = *(ushort*)(cmd + 1);
    else {
        addr = *(ulong*)(cmd + 1);
        if (pfixup == NULL) pfixup = cmd + 1;
        da->fixupsize += 4;
    };
    da->adrconst = addr;
    if (addr == 0) da->zeroconst = 1;
    if (mode >= DISASM_FILE) {
        Memadr(SEG_DS, "", addr, datasize);
    };
};

// Decodes jump relative to nextip of size offsize.
static void DecodeRJ(ulong offsize, ulong nextip) {
    int i;
    ulong addr;
    char s[TEXTLEN];
    if (size < offsize + 1) {
        da->error = DAE_CROSS; return;
    };
    dispsize = offsize;                    // Interpret offset as displacement
    if (mode < DISASM_DATA) return;
    if (offsize == 1)
        addr = (signed char)cmd[1] + nextip;
    else if (offsize == 2)
        addr = *(signed short*)(cmd + 1) + nextip;
    else
        addr = *(ulong*)(cmd + 1) + nextip;
    if (datasize == 2)
        addr &= 0xFFFF;
    da->jmpconst = addr;
    if (addr == 0) da->zeroconst = 1;
    if (mode >= DISASM_FILE) {
        if (offsize == 1) nresult += sprintf(da->result + nresult,
            "%s ", (lowercase == 0 ? "SHORT" : "short"));
        if (mode >= DISASM_CODE)
            i = Decodeaddress(addr, s, TEXTLEN, da->comment);
        else
            i = 0;
        if (symbolic == 0 || i == 0)
            nresult += sprintf(da->result + nresult, "%08lX", addr);
        else
            nresult += sprintf(da->result + nresult, "%.*s", TEXTLEN - nresult - 25, s);
        if (symbolic == 0 && i != 0 && da->comment[0] == '\0')
            strcpy(da->comment, s);
        ;
    };
};

// Decode immediate absolute far jump address. In flat model, such addresses
// are not used (mostly because selector is specified directly in the command),
// so I neither decode as symbol nor comment it. To allow search for selector
// by value, I interprete it as an immediate constant.
static void DecodeJF(void) {
    ulong addr, seg;
    if (size < 1 + addrsize + 2) {
        da->error = DAE_CROSS; return;
    };
    dispsize = addrsize; immsize = 2;        // Non-trivial but allowed interpretation
    if (mode < DISASM_DATA) return;
    if (addrsize == 2) {
        addr = *(ushort*)(cmd + 1);
        seg = *(ushort*)(cmd + 3);
    }
    else {
        addr = *(ulong*)(cmd + 1);
        seg = *(ushort*)(cmd + 5);
    };
    da->jmpconst = addr;
    da->immconst = seg;
    if (addr == 0 || seg == 0) da->zeroconst = 1;
    if (mode >= DISASM_FILE) {
        nresult += sprintf(da->result + nresult, "%s %04X:%08X",
            (lowercase == 0 ? "FAR" : "far"), seg, addr);
    };
};

// Decode segment register. In flat model, operands of this type are seldom.
static void DecodeSG(int index) {
    int i;
    if (mode < DISASM_DATA) return;
    index &= 0x07;
    if (index >= 6) softerror = DAE_BADSEG;  // Undefined segment register
    if (mode >= DISASM_FILE) {
        i = sprintf(da->result + nresult, "%s", segname[index]);
        if (lowercase) strlwr(da->result + nresult);
        nresult += i;
    };
};

// Decode control register addressed in R part of ModRM byte. Operands of
// this type are extremely rare. Contents of control registers are accessible
// only from privilege level 0, so I cannot dump them here.
static void DecodeCR(int index) {
    hasrm = 1;
    if (mode >= DISASM_FILE) {
        index = (index >> 3) & 0x07;
        nresult += sprintf(da->result + nresult, "%s", crname[index]);
        if (lowercase) strlwr(da->result + nresult);
    };
};

// Decode debug register addressed in R part of ModRM byte. Operands of
// this type are extremely rare. I can dump only those debug registers
// available in CONTEXT structure.
static void DecodeDR(int index) {
    int i;
    hasrm = 1;
    if (mode >= DISASM_FILE) {
        index = (index >> 3) & 0x07;
        i = sprintf(da->result + nresult, "%s", drname[index]);
        if (lowercase) strlwr(da->result + nresult);
        nresult += i;
    };
};

// Skips 3DNow! operands and extracts command suffix. Returns suffix or -1 if
// suffix lies outside the memory block. This subroutine assumes that cmd still
// points to the beginning of 3DNow! command (i.e. to the sequence of two bytes
// 0F, 0F).
static int Get3dnowsuffix(void) {
    int c, sib;
    ulong offset;
    if (size < 3) return -1;               // Suffix outside the memory block
    offset = 3;
    c = cmd[2] & 0xC7;                     // Leave only Mod and M fields
    // Register in ModM - general-purpose, MMX or 3DNow!
    if ((c & 0xC0) == 0xC0)
        ;
    // 16-bit addressing mode, SIB byte is never used here.
    else if (addrsize == 2) {
        if (c == 0x06)                       // Special case of immediate address
            offset += 2;
        else if ((c & 0xC0) == 0x40)         // 8-bit signed displacement
            offset++;
        else if ((c & 0xC0) == 0x80)         // 16-bit unsigned displacement
            offset += 2;
        ;
    }
    // Immediate 32-bit address.
    else if (c == 0x05)                    // Special case of immediate address
        offset += 4;
    // 32-bit address with SIB byte.
    else if ((c & 0x07) == 0x04) {         // SIB addresation
        if (size < 4) return -1;             // Suffix outside the memory block
        sib = cmd[3]; offset++;
        if (c == 0x04 && (sib & 0x07) == 0x05)
            offset += 4;                       // Immediate address without base
        else if ((c & 0xC0) == 0x40)         // 8-bit displacement
            offset += 1;
        else if ((c & 0xC0) == 0x80)         // 32-bit dislacement
            offset += 4;
        ;
    }
    // 32-bit address without SIB byte
    else if ((c & 0xC0) == 0x40)
        offset += 1;
    else if ((c & 0xC0) == 0x80)
        offset += 4;
    if (offset >= size) return -1;         // Suffix outside the memory block
    return cmd[offset];
};

// Function checks whether 80x86 flags meet condition set in the command.
// Returns 1 if condition is met, 0 if not and -1 in case of error (which is
// not possible).
int Checkcondition(int code, ulong flags) {
    ulong cond, temp;
    switch (code & 0x0E) {
    case 0:                            // If overflow
        cond = flags & 0x0800; break;
    case 2:                            // If below
        cond = flags & 0x0001; break;
    case 4:                            // If equal
        cond = flags & 0x0040; break;
    case 6:                            // If below or equal
        cond = flags & 0x0041; break;
    case 8:                            // If sign
        cond = flags & 0x0080; break;
    case 10:                           // If parity
        cond = flags & 0x0004; break;
    case 12:                           // If less
        temp = flags & 0x0880;
        cond = (temp == 0x0800 || temp == 0x0080); break;
    case 14:                           // If less or equal
        temp = flags & 0x0880;
        cond = (temp == 0x0800 || temp == 0x0080 || (flags & 0x0040) != 0); break;
    default: return -1;                // Internal error, not possible!
    };
    if ((code & 0x01) == 0) return (cond != 0);
    else return (cond == 0);               // Invert condition
};

ulong Disasm(char* src, ulong srcsize, ulong srcip,
    t_disasm* disasm, int disasmmode) {
    int i, j, isprefix, is3dnow, repeated, operand, mnemosize, arg;
    ulong u, code;
    int lockprefix;                      // Non-zero if lock prefix present
    int repprefix;                       // REPxxx prefix or 0
    int cxsize;
    char name[TEXTLEN], * pname;
    const t_cmddata* pd, * pdan;
    // Prepare disassembler variables and initialize structure disasm.
    datasize = addrsize = 4;                 // 32-bit code and data segments only!
    segprefix = SEG_UNDEF;
    hasrm = hassib = 0; dispsize = immsize = 0;
    lockprefix = 0; repprefix = 0;
    ndump = 0; nresult = 0;
    cmd = src; size = srcsize; pfixup = NULL;
    softerror = 0; is3dnow = 0;
    da = disasm;
    da->ip = srcip;
    da->comment[0] = '\0';
    da->cmdtype = C_BAD; da->nprefix = 0;
    da->memtype = DEC_UNKNOWN; da->indexed = 0;
    da->jmpconst = 0; da->jmptable = 0;
    da->adrconst = 0; da->immconst = 0;
    da->zeroconst = 0;
    da->fixupoffset = 0; da->fixupsize = 0;
    da->warnings = 0;
    da->error = DAE_NOERR;
    mode = disasmmode;                     // No need to use register contents
    // Correct 80x86 command may theoretically contain up to 4 prefixes belonging
    // to different prefix groups. This limits maximal possible size of the
    // command to MAXCMDSIZE=16 bytes. In order to maintain this limit, if
    // Disasm() detects second prefix from the same group, it flushes first
    // prefix in the sequence as a pseudocommand.
    u = 0; repeated = 0;
    while (size > 0) {
        isprefix = 1;                        // Assume that there is some prefix
        switch (*cmd) {
        case 0x26: if (segprefix == SEG_UNDEF) segprefix = SEG_ES;
                 else repeated = 1; break;
        case 0x2E: if (segprefix == SEG_UNDEF) segprefix = SEG_CS;
                 else repeated = 1; break;
        case 0x36: if (segprefix == SEG_UNDEF) segprefix = SEG_SS;
                 else repeated = 1; break;
        case 0x3E: if (segprefix == SEG_UNDEF) segprefix = SEG_DS;
                 else repeated = 1; break;
        case 0x64: if (segprefix == SEG_UNDEF) segprefix = SEG_FS;
                 else repeated = 1; break;
        case 0x65: if (segprefix == SEG_UNDEF) segprefix = SEG_GS;
                 else repeated = 1; break;
        case 0x66: if (datasize == 4) datasize = 2;
                 else repeated = 1; break;
        case 0x67: if (addrsize == 4) addrsize = 2;
                 else repeated = 1; break;
        case 0xF0: if (lockprefix == 0) lockprefix = 0xF0;
                 else repeated = 1; break;
        case 0xF2: if (repprefix == 0) repprefix = 0xF2;
                 else repeated = 1; break;
        case 0xF3: if (repprefix == 0) repprefix = 0xF3;
                 else repeated = 1; break;
        default: isprefix = 0; break;
        };
        if (isprefix == 0 || repeated != 0)
            break;                           // No more prefixes or duplicated prefix
        if (mode >= DISASM_FILE)
            ndump += sprintf(da->dump + ndump, "%02X:", *cmd);
        da->nprefix++;
        cmd++; srcip++; size--; u++;
    };
    // We do have repeated prefix. Flush first prefix from the sequence.
    if (repeated) {
        if (mode >= DISASM_FILE) {
            da->dump[3] = '\0';                // Leave only first dumped prefix
            da->nprefix = 1;
            switch (cmd[-(long)u]) {
            case 0x26: pname = (char*)(segname[SEG_ES]); break;
            case 0x2E: pname = (char*)(segname[SEG_CS]); break;
            case 0x36: pname = (char*)(segname[SEG_SS]); break;
            case 0x3E: pname = (char*)(segname[SEG_DS]); break;
            case 0x64: pname = (char*)(segname[SEG_FS]); break;
            case 0x65: pname = (char*)(segname[SEG_GS]); break;
            case 0x66: pname = "DATASIZE"; break;
            case 0x67: pname = "ADDRSIZE"; break;
            case 0xF0: pname = "LOCK"; break;
            case 0xF2: pname = "REPNE"; break;
            case 0xF3: pname = "REPE"; break;
            default: pname = "?"; break;
            };
            nresult += sprintf(da->result + nresult, "PREFIX %s:", pname);
            if (lowercase) strlwr(da->result);
            if (extraprefix == 0) strcpy(da->comment, "Superfluous prefix");
        };
        da->warnings |= DAW_PREFIX;
        if (lockprefix) da->warnings |= DAW_LOCK;
        da->cmdtype = C_RARE;
        return 1;                          // Any prefix is 1 byte long
    };
    // If lock prefix available, display it and forget, because it has no
    // influence on decoding of rest of the command.
    if (lockprefix != 0) {
        if (mode >= DISASM_FILE) nresult += sprintf(da->result + nresult, "LOCK ");
        da->warnings |= DAW_LOCK;
    };
    // Fetch (if available) first 3 bytes of the command, add repeat prefix and
    // find command in the command table.
    code = 0;
    if (size > 0) *(((char*)&code) + 0) = cmd[0];
    if (size > 1) *(((char*)&code) + 1) = cmd[1];
    if (size > 2) *(((char*)&code) + 2) = cmd[2];
    if (repprefix != 0)                    // RER/REPE/REPNE is considered to be
        code = (code << 8) | repprefix;        // part of command.
    if (decodevxd && (code & 0xFFFF) == 0x20CD)
        pd = &vxdcmd;                        // Decode VxD call (Win95/98)
    else {
        for (pd = cmddata; pd->mask != 0; pd++) {
            if (((code ^ pd->code) & pd->mask) != 0) continue;
            if (mode >= DISASM_FILE && shortstringcmds &&
                (pd->arg1 == MSO || pd->arg1 == MDE || pd->arg2 == MSO || pd->arg2 == MDE))
                continue;                      // Search short form of string command
            break;
        };
    };
    if ((pd->type & C_TYPEMASK) == C_NOW) {
        // 3DNow! commands require additional search.
        is3dnow = 1;
        j = Get3dnowsuffix();
        if (j < 0)
            da->error = DAE_CROSS;
        else {
            for (; pd->mask != 0; pd++) {
                if (((code ^ pd->code) & pd->mask) != 0) continue;
                if (((uchar*)&(pd->code))[2] == j) break;
            };
        };
    };
    if (pd->mask == 0) {                   // Command not found
        da->cmdtype = C_BAD;
        if (size < 2) da->error = DAE_CROSS;
        else da->error = DAE_BADCMD;
    }
    else {                               // Command recognized, decode it
        da->cmdtype = pd->type;
        cxsize = datasize;                   // Default size of ECX used as counter
        if (segprefix == SEG_FS || segprefix == SEG_GS || lockprefix != 0)
            da->cmdtype |= C_RARE;             // These prefixes are rare
        if (pd->bits == PR)
            da->warnings |= DAW_PRIV;          // Privileged command (ring 0)
        else if (pd->bits == WP)
            da->warnings |= DAW_IO;            // I/O command
        // Win32 programs usually try to keep stack dword-aligned, so INC ESP
        // (44) and DEC ESP (4C) usually don't appear in real code. Also check for
        // ADD ESP,imm and SUB ESP,imm (81,C4,imm32; 83,C4,imm8; 81,EC,imm32;
        // 83,EC,imm8).
        if (cmd[0] == 0x44 || cmd[0] == 0x4C ||
            (size >= 3 && (cmd[0] == 0x81 || cmd[0] == 0x83) &&
                (cmd[1] == 0xC4 || cmd[1] == 0xEC) && (cmd[2] & 0x03) != 0)
            ) {
            da->warnings |= DAW_STACK;
            da->cmdtype |= C_RARE;
        };
        // Warn also on MOV SEG,... (8E...). Win32 works in flat mode.
        if (cmd[0] == 0x8E)
            da->warnings |= DAW_SEGMENT;
        // If opcode is 2-byte, adjust command.
        if (pd->len == 2) {
            if (size == 0) da->error = DAE_CROSS;
            else {
                if (mode >= DISASM_FILE)
                    ndump += sprintf(da->dump + ndump, "%02X", *cmd);
                cmd++; srcip++; size--;
            };
        };
        if (size == 0) da->error = DAE_CROSS;
        // Some commands either feature non-standard data size or have bit which
        // allowes to select data size.
        if ((pd->bits & WW) != 0 && (*cmd & WW) == 0)
            datasize = 1;                      // Bit W in command set to 0
        else if ((pd->bits & W3) != 0 && (*cmd & W3) == 0)
            datasize = 1;                      // Another position of bit W
        else if ((pd->bits & FF) != 0)
            datasize = 2;                      // Forced word (2-byte) size
        // Some commands either have mnemonics which depend on data size (8/16 bits
        // or 32 bits, like CWD/CDQ), or have several different mnemonics (like
        // JNZ/JNE). First case is marked by either '&' (mnemonic depends on
        // operand size) or '$' (depends on address size). In the second case,
        // there is no special marker and disassembler selects main mnemonic.
        if (mode >= DISASM_FILE) {
            if (pd->name[0] == '&') mnemosize = datasize;
            else if (pd->name[0] == '$') mnemosize = addrsize;
            else mnemosize = 0;
            if (mnemosize != 0) {
                for (i = 0, j = 1; pd->name[j] != '\0'; j++) {
                    if (pd->name[j] == ':') {      // Separator between 16/32 mnemonics
                        if (mnemosize == 4) i = 0;
                        else break;
                    }
                    else if (pd->name[j] == '*') { // Substitute by 'W', 'D' or none
                        if (mnemosize == 4 && sizesens != 2) name[i++] = 'D';
                        else if (mnemosize != 4 && sizesens != 0) name[i++] = 'W';
                    }
                    else name[i++] = pd->name[j];
                };
                name[i] = '\0';
            }
            else {
                strcpy(name, pd->name);
                for (i = 0; name[i] != '\0'; i++) {
                    if (name[i] == ',') {          // Use main mnemonic
                        name[i] = '\0'; break;
                    };
                };
            };
            if (repprefix != 0 && tabarguments) {
                for (i = 0; name[i] != '\0' && name[i] != ' '; i++)
                    da->result[nresult++] = name[i];
                if (name[i] == ' ') {
                    da->result[nresult++] = ' '; i++;
                };
                while (nresult < 8) da->result[nresult++] = ' ';
                for (; name[i] != '\0'; i++)
                    da->result[nresult++] = name[i];
                ;
            }
            else
                nresult += sprintf(da->result + nresult, "%s", name);
            if (lowercase) strlwr(da->result);
        };
        // Decode operands (explicit - encoded in command, implicit - present in
        // mmemonic or assumed - used or modified by command). Assumed operands
        // must stay after all explicit and implicit operands. Up to 3 operands
        // are allowed.
        for (operand = 0; operand < 3; operand++) {
            if (da->error) break;            // Error - no sense to continue
            // If command contains both source and destination, one usually must not
            // decode destination to comment because it will be overwritten on the
            // next step. Global addcomment takes care of this. Decoding routines,
            // however, may ignore this flag.
            if (operand == 0 && pd->arg2 != NNN && pd->arg2 < PSEUDOOP)
                addcomment = 0;
            else
                addcomment = 1;
            // Get type of next argument.
            if (operand == 0) arg = pd->arg1;
            else if (operand == 1) arg = pd->arg2;
            else arg = pd->arg3;
            if (arg == NNN) break;             // No more operands
            // Arguments with arg>=PSEUDOOP are assumed operands and are not
            // displayed in disassembled result, so they require no delimiter.
            if ((mode >= DISASM_FILE) && arg < PSEUDOOP) {
                if (operand == 0) {
                    da->result[nresult++] = ' ';
                    if (tabarguments) {
                        while (nresult < 8) da->result[nresult++] = ' ';
                    };
                }
                else {
                    da->result[nresult++] = ',';
                    if (extraspace) da->result[nresult++] = ' ';
                };
            };
            // Decode, analyse and comment next operand of the command.
            switch (arg) {
            case REG:                      // Integer register in Reg field
                if (size < 2) da->error = DAE_CROSS;
                else DecodeRG(cmd[1] >> 3, datasize, REG);
                hasrm = 1; break;
            case RCM:                      // Integer register in command byte
                DecodeRG(cmd[0], datasize, RCM); break;
            case RG4:                      // Integer 4-byte register in Reg field
                if (size < 2) da->error = DAE_CROSS;
                else DecodeRG(cmd[1] >> 3, 4, RG4);
                hasrm = 1; break;
            case RAC:                      // Accumulator (AL/AX/EAX, implicit)
                DecodeRG(REG_EAX, datasize, RAC); break;
            case RAX:                      // AX (2-byte, implicit)
                DecodeRG(REG_EAX, 2, RAX); break;
            case RDX:                      // DX (16-bit implicit port address)
                DecodeRG(REG_EDX, 2, RDX); break;
            case RCL:                      // Implicit CL register (for shifts)
                DecodeRG(REG_ECX, 1, RCL); break;
            case RS0:                      // Top of FPU stack (ST(0))
                DecodeST(0, 0); break;
            case RST:                      // FPU register (ST(i)) in command byte
                DecodeST(cmd[0], 0); break;
            case RMX:                      // MMX register MMx
                if (size < 2) da->error = DAE_CROSS;
                else DecodeMX(cmd[1] >> 3);
                hasrm = 1; break;
            case R3D:                      // 3DNow! register MMx
                if (size < 2) da->error = DAE_CROSS;
                else DecodeNR(cmd[1] >> 3);
                hasrm = 1; break;
            case MRG:                      // Memory/register in ModRM byte
            case MRJ:                      // Memory/reg in ModRM as JUMP target
            case MR1:                      // 1-byte memory/register in ModRM byte
            case MR2:                      // 2-byte memory/register in ModRM byte
            case MR4:                      // 4-byte memory/register in ModRM byte
            case MR8:                      // 8-byte memory/MMX register in ModRM
            case MRD:                      // 8-byte memory/3DNow! register in ModRM
            case MMA:                      // Memory address in ModRM byte for LEA
            case MML:                      // Memory in ModRM byte (for LES)
            case MM6:                      // Memory in ModRm (6-byte descriptor)
            case MMB:                      // Two adjacent memory locations (BOUND)
            case MD2:                      // Memory in ModRM byte (16-bit integer)
            case MB2:                      // Memory in ModRM byte (16-bit binary)
            case MD4:                      // Memory in ModRM byte (32-bit integer)
            case MD8:                      // Memory in ModRM byte (64-bit integer)
            case MDA:                      // Memory in ModRM byte (80-bit BCD)
            case MF4:                      // Memory in ModRM byte (32-bit float)
            case MF8:                      // Memory in ModRM byte (64-bit float)
            case MFA:                      // Memory in ModRM byte (80-bit float)
            case MFE:                      // Memory in ModRM byte (FPU environment)
            case MFS:                      // Memory in ModRM byte (FPU state)
            case MFX:                      // Memory in ModRM byte (ext. FPU state)
                DecodeMR(arg); break;
            case MMS:                      // Memory in ModRM byte (as SEG:OFFS)
                DecodeMR(arg);
                da->warnings |= DAW_FARADDR; break;
            case RR4:                      // 4-byte memory/register (register only)
            case RR8:                      // 8-byte MMX register only in ModRM
            case RRD:                      // 8-byte memory/3DNow! (register only)
                if ((cmd[1] & 0xC0) != 0xC0) softerror = DAE_REGISTER;
                DecodeMR(arg); break;
            case MSO:                      // Source in string op's ([ESI])
                DecodeSO(); break;
            case MDE:                      // Destination in string op's ([EDI])
                DecodeDE(); break;
            case MXL:                      // XLAT operand ([EBX+AL])
                DecodeXL(); break;
            case IMM:                      // Immediate data (8 or 16/32)
            case IMU:                      // Immediate unsigned data (8 or 16/32)
                if ((pd->bits & SS) != 0 && (*cmd & 0x02) != 0)
                    DecodeIM(1, datasize, arg);
                else
                    DecodeIM(datasize, 0, arg);
                break;
            case VXD:                      // VxD service (32-bit only)
                DecodeVX(); break;
            case IMX:                      // Immediate sign-extendable byte
                DecodeIM(1, datasize, arg); break;
            case C01:                      // Implicit constant 1 (for shifts)
                DecodeC1(); break;
            case IMS:                      // Immediate byte (for shifts)
            case IM1:                      // Immediate byte
                DecodeIM(1, 0, arg); break;
            case IM2:                      // Immediate word (ENTER/RET)
                DecodeIM(2, 0, arg);
                if ((da->immconst & 0x03) != 0) da->warnings |= DAW_STACK;
                break;
            case IMA:                      // Immediate absolute near data address
                DecodeIA(); break;
            case JOB:                      // Immediate byte offset (for jumps)
                DecodeRJ(1, srcip + 2); break;
            case JOW:                      // Immediate full offset (for jumps)
                DecodeRJ(datasize, srcip + datasize + 1); break;
            case JMF:                      // Immediate absolute far jump/call addr
                DecodeJF();
                da->warnings |= DAW_FARADDR; break;
            case SGM:                      // Segment register in ModRM byte
                if (size < 2) da->error = DAE_CROSS;
                DecodeSG(cmd[1] >> 3); hasrm = 1; break;
            case SCM:                      // Segment register in command byte
                DecodeSG(cmd[0] >> 3);
                if ((da->cmdtype & C_TYPEMASK) == C_POP) da->warnings |= DAW_SEGMENT;
                break;
            case CRX:                      // Control register CRx
                if ((cmd[1] & 0xC0) != 0xC0) da->error = DAE_REGISTER;
                DecodeCR(cmd[1]); break;
            case DRX:                      // Debug register DRx
                if ((cmd[1] & 0xC0) != 0xC0) da->error = DAE_REGISTER;
                DecodeDR(cmd[1]); break;
            case PRN:                      // Near return address (pseudooperand)
                break;
            case PRF:                      // Far return address (pseudooperand)
                da->warnings |= DAW_FARADDR; break;
            case PAC:                      // Accumulator (AL/AX/EAX, pseudooperand)
                DecodeRG(REG_EAX, datasize, PAC); break;
            case PAH:                      // AH (in LAHF/SAHF, pseudooperand)
            case PFL:                      // Lower byte of flags (pseudooperand)
                break;
            case PS0:                      // Top of FPU stack (pseudooperand)
                DecodeST(0, 1); break;
            case PS1:                      // ST(1) (pseudooperand)
                DecodeST(1, 1); break;
            case PCX:                      // CX/ECX (pseudooperand)
                DecodeRG(REG_ECX, cxsize, PCX); break;
            case PDI:                      // EDI (pseudooperand in MMX extentions)
                DecodeRG(REG_EDI, 4, PDI); break;
            default:
                da->error = DAE_INTERN;        // Unknown argument type
                break;
            };
        };
        // Check whether command may possibly contain fixups.
        if (pfixup != NULL && da->fixupsize > 0)
            da->fixupoffset = pfixup - src;
        // Segment prefix and address size prefix are superfluous for command which
        // does not access memory. If this the case, mark command as rare to help
        // in analysis.
        if (da->memtype == DEC_UNKNOWN &&
            (segprefix != SEG_UNDEF || (addrsize != 4 && pd->name[0] != '$'))
            ) {
            da->warnings |= DAW_PREFIX;
            da->cmdtype |= C_RARE;
        };
        // 16-bit addressing is rare in 32-bit programs. If this is the case,
        // mark command as rare to help in analysis.
        if (addrsize != 4) da->cmdtype |= C_RARE;
    };
    // Suffix of 3DNow! command is accounted best by assuming it immediate byte
    // constant.
    if (is3dnow) {
        if (immsize != 0) da->error = DAE_BADCMD;
        else immsize = 1;
    };
    // Right or wrong, command decoded. Now dump it.
    if (da->error != 0) {                  // Hard error in command detected
        if (mode >= DISASM_FILE)
            nresult = sprintf(da->result, "???");
        if (da->error == DAE_BADCMD &&
            (*cmd == 0x0F || *cmd == 0xFF) && size > 0
            ) {
            if (mode >= DISASM_FILE) ndump += sprintf(da->dump + ndump, "%02X", *cmd);
            cmd++; size--;
        };
        if (size > 0) {
            if (mode >= DISASM_FILE) ndump += sprintf(da->dump + ndump, "%02X", *cmd);
            cmd++; size--;
        };
    }
    else {                               // No hard error, dump command
        if (mode >= DISASM_FILE) {
            ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
            if (hasrm) ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
            if (hassib) ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
            if (dispsize != 0) {
                da->dump[ndump++] = ' ';
                for (i = 0; i < dispsize; i++) {
                    ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
                };
            };
            if (immsize != 0) {
                da->dump[ndump++] = ' ';
                for (i = 0; i < immsize; i++) {
                    ndump += sprintf(da->dump + ndump, "%02X", *cmd++);
                };
            };
        }
        else
            cmd += 1 + hasrm + hassib + dispsize + immsize;
        size -= 1 + hasrm + hassib + dispsize + immsize;
    };
    // Check that command is not a dangerous one.
    if (mode >= DISASM_DATA) {
        for (pdan = dangerous; pdan->mask != 0; pdan++) {
            if (((code ^ pdan->code) & pdan->mask) != 0)
                continue;
            if (pdan->type == C_DANGERLOCK && lockprefix == 0)
                break;                         // Command harmless without LOCK prefix
            if (iswindowsnt && pdan->type == C_DANGER95)
                break;                         // Command harmless under Windows NT
            // Dangerous command!
            if (pdan->type == C_DANGER95) da->warnings |= DAW_DANGER95;
            else da->warnings |= DAW_DANGEROUS;
            break;
        };
    };
    if (da->error == 0 && softerror != 0)
        da->error = softerror;               // Error, but still display command
    if (mode >= DISASM_FILE) {
        if (da->error != DAE_NOERR) switch (da->error) {
        case DAE_CROSS:
            strcpy(da->comment, "Command crosses end of memory block"); break;
        case DAE_BADCMD:
            strcpy(da->comment, "Unknown command"); break;
        case DAE_BADSEG:
            strcpy(da->comment, "Undefined segment register"); break;
        case DAE_MEMORY:
            strcpy(da->comment, "Illegal use of register"); break;
        case DAE_REGISTER:
            strcpy(da->comment, "Memory address not allowed"); break;
        case DAE_INTERN:
            strcpy(da->comment, "Internal OLLYDBG error"); break;
        default:
            strcpy(da->comment, "Unknown error");
            break;
        }
        else if ((da->warnings & DAW_PRIV) != 0 && privileged == 0)
            strcpy(da->comment, "Privileged command");
        else if ((da->warnings & DAW_IO) != 0 && iocommand == 0)
            strcpy(da->comment, "I/O command");
        else if ((da->warnings & DAW_FARADDR) != 0 && farcalls == 0) {
            if ((da->cmdtype & C_TYPEMASK) == C_JMP)
                strcpy(da->comment, "Far jump");
            else if ((da->cmdtype & C_TYPEMASK) == C_CAL)
                strcpy(da->comment, "Far call");
            else if ((da->cmdtype & C_TYPEMASK) == C_RET)
                strcpy(da->comment, "Far return");
            ;
        }
        else if ((da->warnings & DAW_SEGMENT) != 0 && farcalls == 0)
            strcpy(da->comment, "Modification of segment register");
        else if ((da->warnings & DAW_SHIFT) != 0 && badshift == 0)
            strcpy(da->comment, "Shift constant out of range 1..31");
        else if ((da->warnings & DAW_PREFIX) != 0 && extraprefix == 0)
            strcpy(da->comment, "Superfluous prefix");
        else if ((da->warnings & DAW_LOCK) != 0 && lockedbus == 0)
            strcpy(da->comment, "LOCK prefix");
        else if ((da->warnings & DAW_STACK) != 0 && stackalign == 0)
            strcpy(da->comment, "Unaligned stack operation");
        ;
    };
    return (srcsize - size);               // Returns number of recognized bytes
};
